package org.report4service.dbutils;

import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

import org.report4service.listeners.StorageListener;
import org.report4service.utils.Serializer;

/**
 * Handles storage and retrieval of objects of various types to RMS.
 * 
 * @author Jmpango
 * 
 */
public abstract class RMSStorage {

    /** Reference to the recodStore */
    protected volatile RecordStore recordStore = null;
    /** The vector of used recordIDs that are in the datase */
    protected volatile Vector recordIDs = null;
    /** The last used ID in the database */
    protected int lastID = 1;
    /** Reference to the event listner */
    protected StorageListener eventListener;
    /** Object used to compare two records and see if they are equal */
    protected RecordComparator rc;
    /** To perform conversion between bytes and objects */
    private Serializer serializer;

    /** Default Constructor */
    public RMSStorage() {
        serializer = new Serializer();
    }

    /**
     * Close the recordStore and removes it from the persistant storage if its
     * empty
     */
    public boolean closeDB() throws RecordStoreNotOpenException,
            RecordStoreException {
        if (recordStore.getNumRecords() == 0) {
            String fileName = recordStore.getName();
            recordStore.closeRecordStore();
            RecordStore.deleteRecordStore(fileName);
        } else {
            recordStore.closeRecordStore();
        }
        return true;
    }

    /**
     * Deletes a specific record from the DB
     */
    public synchronized boolean delete(String record) throws RecordStoreNotOpenException,
            RecordStoreException,
            RecordStoreFullException,
            IOException {
        Object obj = action(record, null, 0);
        if (obj != null) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Go to the index of the record and perform an action
     * 
     * @param record
     * @param object
     * @param i
     */
    private synchronized Object action(String record, String newRecord, int action)
            throws RecordStoreNotOpenException, RecordStoreFullException,
            RecordStoreException, IOException {
        if ((action != 1) && recordIDs.size() == 0) {
            throw new RecordStoreException();
        }

        Enumeration IDs = recordIDs.elements();
        while (IDs.hasMoreElements()) {
            int index = ((Integer) IDs.nextElement()).intValue();
            try {
                if (rc.compare(recordStore.getRecord(index), serializer.serialise(record)) == RecordComparator.EQUIVALENT) {
                    switch (action) {
                        case 0:
                            recordStore.deleteRecord(index);
                            recordIDs.removeElement(new Integer(index));
                            break;
                        case 1:
                            return new String(recordStore.getRecord(index));
                        case 2:
                            try {
                                byte[] data = serializer.serialise(newRecord);
                                recordStore.setRecord(index, data, 0, data.length);
                                return new String(recordStore.getRecord(index));
                            } catch (IOException ex) {
                                ex.printStackTrace();
                            }

                        default:
                            break;
                    }
                }
            } catch (InvalidRecordIDException iri) {
                eventListener.errorOccured("errors:", iri);
            }
        }
        return null;
    }

    /**
     * Gets the number of rows in a record.
     */
    public int getNumRecords() throws RecordStoreNotOpenException {
        if (recordStore != null) {
            return recordStore.getNumRecords();
        } else {
            return 0;
        }
    }

    /**
     * Opens up a database
     */
    public boolean openDB(String dbName) throws RecordStoreNotFoundException,
            RecordStoreException,
            RecordStoreFullException {

        if (recordStore != null) {
            recordStore.closeRecordStore();
            recordStore = null;
            openDB(dbName);
        } else {

            recordStore = RecordStore.openRecordStore(dbName, true);
            recordIDs = new Vector();
            try {
                if (recordStore.getNumRecords() != 0) {
                    try {
                        lastID = Integer.parseInt(serializer.deserialise(recordStore.getRecord(1)));
                        for (int i = 1; i <= lastID; i++) {
                            recordIDs.addElement(new Integer(i));
                        }

                    } catch (IOException ex) {
                        ex.printStackTrace();
                    } catch (InvalidRecordIDException e) {
                        eventListener.errorOccured("error:", e);
                    }
                    return true;
                }
            } catch (RecordStoreNotOpenException e) {
                eventListener.errorOccured("error:", e);
            }
        }
        return false;
    }

    /**
     * Add a record to a Database.
     */
    public synchronized boolean addDbRecord(String record) throws RecordStoreNotOpenException,
            RecordStoreFullException,
            RecordStoreException {

        try {
            if (recordStore.getNumRecords() != 0) {
                byte[] data = serializer.serialise(record);
                recordStore.addRecord(data, 0, data.length);
                recordIDs.addElement(new Integer(++lastID));
                byte[] data1 = serializer.serialise(String.valueOf(lastID));
                recordStore.setRecord(1, data1, 0, data1.length);
                data = null;
                data1 = null;
            } else {
                recordIDs.addElement(new Integer(++lastID));
                byte[] data = serializer.serialise(String.valueOf(lastID));
                recordStore.addRecord(data, 0, data.length);
                data = null;

                try {
                    byte[] data2 = serializer.serialise(record);
                    recordStore.addRecord(data2, 0, data2.length);
                    data2 = null;
                } catch (RecordStoreException e) {
                    recordIDs.removeElement(new Integer(lastID--));
                    byte[] data3 = serializer.serialise(String.valueOf(lastID));
                    recordStore.setRecord(1, data3, 0, data3.length);
                    data3 = null;
                    throw e;
                }
            }
            return true;
        } catch (Exception e) {
            eventListener.errorOccured("error:", e);
        }

        return false;
    }

    /**
     * Updates a record.
     */
    public synchronized boolean updateDbRecord(String record, String newRecord)
            throws RecordStoreNotOpenException, RecordStoreFullException,
            RecordStoreException, IOException {
        Object obj = action(record, newRecord, 2);
        if (obj != null) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Find and return a record.
     */
    public synchronized String searchForRecord(String s)
            throws RecordStoreNotOpenException, RecordStoreException, RecordStoreFullException, IOException {
        return (String) action(s, null, 1);
    }

    /**
     * Removes the database from persistant storage
     */
    public boolean cleanUpDb(String record) throws RecordStoreNotFoundException,
            RecordStoreException {

        try {
            RecordStore.deleteRecordStore(record);
            lastID = 1;
            openDB(record);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Enumerate through the records.
     */
    public RecordEnumeration enumerateRecords() throws RecordStoreNotOpenException {
        return recordStore.enumerateRecords(null, null, false);
    }

    public void cleanDbUp() {
        recordStore = null;
        recordIDs = null;
        lastID = 1;
        eventListener = null;
        rc = null;
        serializer = null;
    }

    /**
     * @return the recordStore
     */
    public RecordStore getRecordStore() {
        return recordStore;
    }

    /**
     * @param recordStore the recordStore to set
     */
    public void setRecordStore(RecordStore recordStore) {
        this.recordStore = recordStore;
    }

    /**
     * @return the recordIDs
     */
    public Vector getRecordIDs() {
        return recordIDs;
    }

    /**
     * @param recordIDs the recordIDs to set
     */
    public void setRecordIDs(Vector recordIDs) {
        this.recordIDs = recordIDs;
    }

    /**
     * @return the lastID
     */
    public int getLastID() {
        return lastID;
    }

    /**
     * @param lastID the lastID to set
     */
    public void setLastID(int lastID) {
        this.lastID = lastID;
    }
}
